package com.leo.app;

import com.leo.Framework;
import com.leo.app.handle.OnExit;
import com.leo.app.handle.OnFinish;
import com.leo.app.handle.OnLaunch;
import com.leo.app.input.KeyInput;
import com.leo.app.input.MouseInput;
import com.leo.helper.ClassHelper;
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName App
 * @Description
 * @Author wangss
 * @date 2020.12.07 19:42
 * @Version 1.0
 */
public class UIApp {

    /**
     * 全局stage
     */
    private final Stage stage;

    /**
     * 全局场景
     */
    private final Scene scene;

    private final Pane root;

    /**
     * 存储页面
     */
    private final Map<String, View> viewMap;
    /**
     * 当前页(可加监听)
     */
    private final ObjectProperty<View> currentView;

    protected OnLaunch onLaunch;
    protected OnFinish onFinish;
    protected OnExit onExit;

    /**
     * 引擎
     */
    private Engine engine;

    /**
     * 输入
     */
    private KeyInput keyInput;
    private MouseInput mouseInput;

    public UIApp(Stage stage) {
        this.stage = stage;
        this.root = new StackPane();
        this.scene = new Scene(root);
        stage.setScene(scene);

        viewMap = new HashMap<>();
        currentView = new SimpleObjectProperty<>();

        engine = new Engine();
        keyInput = new KeyInput();
        mouseInput = new MouseInput();

        initFramework();
        initApp();
        initEngine();
        // 加载所有继承view的子类
        initView();
    }

    /**
     * 初始化framework
     */
    private final void initFramework() {
        Framework.UIApp = this;
        Framework.engine = engine;
        Framework.keyInput = keyInput;
        Framework.mouseInput = mouseInput;
    }

    /**
     * 初始化app
     */
    private final void initApp() {
        // 设置填充颜色和背景
        scene.setFill(Color.WHITE);
        root.setBackground(Background.EMPTY);
        // stage 添加事件，当系统关闭时判断是否可以关闭
        stage.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, event -> {
            if (null != onExit && !onExit.handle()) {
                // 事件取消
                event.consume();
            }
        });
        // 给当前界面加上监听
        currentView.addListener((observable, oldValue, newValue) -> {
            // 如果存在旧的页面
            if (null != oldValue) {
                oldValue.onLeave();
                root.getChildren().remove(oldValue.getPane());
            }
            // 如果存在新的页面
            if (null != newValue) {
                root.getChildren().add(newValue.getPane());
                newValue.onEnter();
            }
        });
    }

    /**
     * 初始化引擎   整个窗体中只有一个 AnimationTimer 在运行，所以内存消耗应该还好
     */
    private final void initEngine() {
        engine.onStart = () -> {
            for (View view : viewMap.values()) {
                view.onStart();
            }

            keyInput.install(stage);
            mouseInput.install(stage);
        };

        // 这里的执行在于 AnimationTimer 启动以后，会自动不停的执行handle方法，然后调用这里
        engine.onUpdate = (time -> {
            View currentView = getCurrentView();
            if (null != currentView) {
                currentView.onUpdate(time);
            }

            keyInput.refresh();
            mouseInput.refresh();
        });

        engine.onStop = () -> {
            keyInput.uninstall(stage);
            mouseInput.uninstall(stage);

            for (View view : viewMap.values()) {
                view.onStop();
            }
        };

        // stage 获得焦点或者失去焦点时的动作
        stage.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) {
                engine.start();
            } else {
                engine.stop();
            }
        });
    }

    /**
     * 加载所有的页面
     */
    private final void initView() {
        try {
            for (Class<?> cls : ClassHelper.getClassSet()) {
                if (!Modifier.isAbstract(cls.getModifiers()) && !Modifier.isInterface(cls.getModifiers()) && View.class.isAssignableFrom(cls)) {
                    regView(cls.getName(), (View) cls.newInstance());
                }
            }
        } catch (Exception e) {

        }
    }

    public View getView(String name) {
        return viewMap.get(name);
    }

    /**
     * 注册界面
     *
     * @param name
     * @param view
     */
    public void regView(String name, View view) {
        viewMap.put(name, view);
    }

    /**
     * 移除界面
     *
     * @param name
     */
    public void unregView(String name) {
        View remove = viewMap.remove(name);
        if (null != remove && remove == getCurrentView()) {
            currentView.set(null);
        }
    }

    public View getCurrentView() {
        return currentView.get();
    }

    public ReadOnlyObjectProperty<View> currentViewProperty() {
        return currentView;
    }

    /**
     * 页面跳转
     *
     * @param name
     */
    public void gotoView(String name) {
        View view = viewMap.get(name);
        if (view != null) {
            currentView.set(view);
        }
    }

    /**
     * 加载（启动）
     */
    protected void launch() {
        if (null != onLaunch) {
            onLaunch.handle();
        }

        for (View view : viewMap.values()) {
            view.onLaunch();
        }

        stage.requestFocus();
        stage.show();
    }

    /**
     * 加载完成
     */
    protected void finish() {
        for (View view : viewMap.values()) {
            view.onFinish();
        }

        if (null != onFinish) {
            onFinish.handle();
        }
    }

    /**
     * 退出，这里的使用是给stage加上了关闭监听
     */
    public void exit() {
        Platform.exit();
    }

    public Stage getStage() {
        return stage;
    }

    public Scene getScene() {
        return scene;
    }

    public Pane getRoot() {
        return root;
    }

    public void setTitle(String value) {
        stage.setTitle(value);
    }

    public String getTitle() {
        return stage.getTitle();
    }

    public StringProperty titleProperty() {
        return stage.titleProperty();
    }

    public double getWidth() {
        return root.getMinWidth();
    }

    public DoubleProperty widthProperty() {
        return root.minWidthProperty();
    }

    public void setWidth(double width) {
        root.setMinWidth(width);
    }

    public double getHeight() {
        return root.getMinHeight();
    }

    public void setHeight(double height) {
        root.setMinHeight(height);
    }

    public DoubleProperty heightProperty() {
        return root.minHeightProperty();
    }
}
